#!/usr/bin/python


from python_interface_to_soprano import *
import logging
logger = logging.getLogger("Pimo")


#**********************************************************#
# This class offers the possibilities from the PIMO Ontology: TODO (to write)
class Pimo:
	
	# @param interface_to_soprano an interface to call the methods : Interface
	def __init__(self, interface_to_soprano):
		
		self.i = interface_to_soprano


							 ##############################               
########################## GETTERS #########################
							 ##############################               


	def getDomainsArray(self, property_uri):
		
		query = "SELECT ?dom WHERE { <" + property_uri + "> <" + RDFS_DOMAIN + "> ?dom }"
		domains = self.i.executeQuery(query)
		
		domains_res = []
		while domains.next():
			domain = domains.current()[0]["dom"][1]
			domains_res.append(domain)
			
		domains.close()
		
		return domains_res

############################################################

	def getDomainTypesOf(self, property_uri):
		
		domains = self.getDomainsArray(property_uri)
		
		return domains
		
############################################################

	def getRangesArray(self, property_uri):
		
		query = "SELECT ?ran WHERE { <" + property_uri + "> <" + RDFS_RANGE + "> ?ran }"
		ranges = self.i.executeQuery(query)
		
		ranges_res = []
		while ranges.next():
			my_range = ranges.current()[0]["ran"][1]
			ranges_res.append(my_range)
		
		ranges.close()
		return ranges_res

############################################################

	# TODO: comment that!
	def getUsageRangesArray(self, property_uri):
		
		query = "SELECT ?dom_elt ?ran_elt WHERE { ?dom_elt <" + property_uri + "> ?ran_elt }"
		ranges_elts = self.i.executeQuery(query)
		
		ranges = []
		while ranges_elts.next():
			range_elt = ranges_elts.current()[0]["ran_elt"][1]
			ran = self.i.getImage(range_elt, RDF_TYPE)
			if ran != None:
				ranges.append(ran)
		
		ranges_elts.close()
		return ranges
	
############################################################

	# TODO: comment that!
	def getRangeTypesOf(self, property_uri):
		
		ranges = self.getRangesArray(property_uri)
		
		return ranges
		
############################################################

	# domain_type_uri and range_type_uri must be both None OR both valid types.
	def getPropertiesArray(self, ontologies_uris, domain_type_uri, range_type_uri):
		
		queryTotTime = None
		compatTotTime = None
		
		properties_res = []
		named_properties = []
		
		for ontology_uri in ontologies_uris:
			
			time = datetime.today()
			query = "SELECT ?prop ?prop_name WHERE { GRAPH <" + ontology_uri + "> { ?prop <" + RDF_TYPE + "> <" + RDF_PROPERTY + "> ?prop <" + RDFS_LABEL + "> ?prop_name } }"
			properties = self.i.executeQuery(query)
			if queryTotTime == None:
				queryTotTime = datetime.today() - time
			else:
				queryTotTime += datetime.today() - time
			
			time = datetime.today()
			while properties.next():
				property = properties.current()[0]["prop"][1]
				prop_name = properties.current()[0]["prop_name"][1]
				if domain_type_uri != None: # Let us check the given types
					compatible = False
					domains = self.getDomainsArray(property)
					for dom in domains:
						if self.i.derivesFrom(domain_type_uri, dom):
							compatible = True
							break
					ranges = self.getRangesArray(property)
					for ran in ranges:
						if self.i.derivesFrom(range_type_uri, ran):
							compatible = True
							break # for loop
					if not compatible:
						continue # next element in while loop (no appending)
				
				# whether there are no given types OR (there are given types AND the types of the current property are compatible with these)
				properties_res.append(property)
				named_properties.append( [prop_name, property] )
				
			if compatTotTime == None:
				compatTotTime = datetime.today() - time
			else:
				compatTotTime += datetime.today() - time
			properties.close()
			
		#gen = rdf_gen(self.i.results_path+"/properties.rdf")
		## TODO: Annotation:url for the second member? (may cause problems with malformed url...)
		#gen.write_list(["Annotation:item", "Annotation:url"], named_properties)
		
		logger.debug("getPropertiesArray:")
		logger.debug("\tqueryTotTime = "+str(queryTotTime))
		logger.debug("\tcompatTotTime = "+str(compatTotTime))
		
		return named_properties

############################################################

	# ontologies_uris : Array(ontology_uri)
	def getAllPropertiesOf(self, ontologies_uris, domain_type_uri=None, range_type_uri=None):
		
		named_properties = self.getPropertiesArray(ontologies_uris, domain_type_uri, range_type_uri)
		
		return named_properties
	
############################################################

	def getOntologiesArray(self):
		
		# TODO
		return ontologies_res
	
############################################################

	def getPossibleRangeElementsArray(self, property_uri):
		
		queryTotTime = None
		
		# TODO: maybe build a list of correct types and then only check if each element has one of these types
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		# build a list of all accepted types, i.e. all sons of all ranges
		accepted_types = self.i.buildDownTree(ranges, RDFS_SUBCLASSOF)
		print str(len(accepted_types))+", "+str(accepted_types)
		
		# TODO: test the execution speed with one request to get all results...
		# TODO: restrict by ontology here too?
		time = datetime.today()
		query = "SELECT ?elt ?type WHERE { ?elt <" + RDF_TYPE + "> ?type }"
		elts = self.i.executeQuery(query)
		if queryTotTime == None:
			queryTotTime = datetime.today() - time
		else:
			queryTotTime += datetime.today() - time
		
		time = datetime.today()
		compatibilities = {}
		elts_res = []
		while elts.next():
			elt = elts.current()[0]["elt"][1]
			elt_type = elts.current()[0]["type"][1]
			
			#if elt_type in compatibilities: # this type's compatibility has already been calculated
				#if compatibilities[elt_type]:
					#elts_res.append(elt)
				
			#else: # calculate the compatibility for this elt_type
				#compatible = False
				#for range_type in ranges:
					#t1 = datetime.today()
					#bool = self.i.derivesFrom(elt_type, range_type)
					#t2 = datetime.today()
					#logger.debug(str(t2-t1))
					#if bool:
						#compatible = True
						#break # for loop
				#if compatible:
					#compatibilities[elt_type] = True
					##print elt_type + " derives from " + str(ranges)
					#elts_res.append(elt)
				#else:
					#compatibilities[elt_type] = False
					##print elt_type + " does not derive from " + str(ranges)
			
			# does not work as expected... yet
			if accepted_types.count(elt_type) >= 1:
				logger.debug(elt_type+" accepted")
				elts_res.append(elt)
			else:
				#logger.debug(elt_type+" not accepted")
				None
		
		
		compatTotTime = datetime.today() - time
		elts.close()
		
		logger.debug("getPossibleRangeElementsArray:")
		logger.debug("\tqueryTotTime = "+str(queryTotTime))
		logger.debug("\tcompatTotTime = "+str(compatTotTime))
		
		return elts_res
			
############################################################

	def getPossibleRangeElementsFor(self, property_uri):
		
		elements = self.getPossibleRangeElementsArray(property_uri)
		
		named_elements = []
		for elt in elements:
			thing_name = self.getThingNameOfThing(elt)
			if thing_name == None:
				thing_name = elt
			named_elements.append([thing_name, elt])
		
		#gen = rdf_gen(self.i.results_path+"/othersList.rdf")
		#gen.write_list(["Annotation:item", "Annotation:url"], named_elements)
		
		return named_elements

############################################################
	
	# possible status values are "Unstable", "Stable" and "Testing"
	def getAllOntologiesArray(self, status = "any"):
		
		if status == "any":
			query = "SELECT ?onto WHERE { ?onto <" + RDF_TYPE + "> <" + NRL_ONTOLOGY + "> }"
		else:
			query = "SELECT ?onto WHERE { ?onto <" + RDF_TYPE + "> <" + NRL_ONTOLOGY + "> ?onto <" + NAO_STATUS + "> \"" + status + "\"^^<" + XMLSCHEMA_STRING + "> }"
		
		ontologies = self.i.executeQuery(query)
		
		ontologies_res = []
		while ontologies.next():
			ontology = ontologies.current()[0]["onto"][1]
			ontologies_res.append(ontology)
		
		ontologies.close()
		return ontologies_res

############################################################

	# returns the ontology containing the given property.
	# not callable through python_calls.
	def getOntologyContaining(self, property_uri):
		
		query = "SELECT * WHERE { GRAPH ?onto " + makeExistenceWhereClause(property_uri, RDF_TYPE, RDF_PROPERTY) + " }"
		logger.debug(query)
		ontologies = self.i.executeQuery(query)
		
		ontology = None
		if ontologies.next():
			ontology = ontologies.current()[0]["onto"][1]
		else:
			ontologies.close()
			return None
		
		if ontologies.next():
			ontology2 = ontologies.current()[0]["onto"][1]
			# there are more than one ontology containing this property!
			ontologies.close()
			raise DatabaseIncoherenceException("The property \"" + property_uri + "\" has been located in more than one ontology (at least \"" + ontology + "\" and \"" + ontology2 + "\").")
		
		ontologies.close()
		return ontology
		
############################################################

	def getThingNameOfResource(self, resource_uri):
		
		query = "SELECT ?thing_uri ?thing_name WHERE { ?thing_uri <" + PIMO_GROUNDINGOCCURRENCE + "> <" + resource_uri + "> ?thing_uri <" + RDFS_LABEL + "> ?thing_name }"
		things = self.i.executeQuery(query)
		
		thing_name = None
		if things.next():
			thing_name = things.current()[0]["thing_name"][1]
		else:
			thing_name = None
		
		if things.next():
			thing_name2 = things.current()[0]["thing_name"][1]
			things.close()
			raise DatabaseIncoherenceException("The uri \"" + resource_uri + "\" has been located as a groundingOccurrence for more than one pimo:Thing (at least \"" + thing_name + "\" and \"" + thing_name2 + "\").")
		
		things.close()
		return thing_name
		
############################################################

	def getThingNameOfThing(self, thing_uri):
		
		query = "SELECT ?thing_name WHERE { <" + thing_uri + "> <" + RDFS_LABEL + "> ?thing_name }"
		thing_names = self.i.executeQuery(query)
		
		thing_name = None
		if thing_names.next():
			thing_name = thing_names.current()[0]["thing_name"][1]
		else:
			thing_name = None
		
		if thing_names.next():
			thing_name2 = thing_names.current()[0]["thing_name"][1]
			thing_names.close()
			raise DatabaseIncoherenceException("The thing \"" + thing_uri + "\" more than one name/label (at least \"" + thing_name + "\" and \"" + thing_name2 + "\").")
		
		thing_names.close()
		return thing_name


							 ##############################               
######################### "SETTERS" ########################
							 ##############################               


	# param value - describes the resource : String
	def createDescribingProperty(self, user_id, property_name): #, value_type_uri=None):
		
		# Create the container Ontology (= storing graph = context) if not already existing.
		if self.i.getContextType(DEFAULT_ONTOLOGY_URI) == None:
			self.i.createContext(DEFAULT_ONTOLOGY_URI, NRL_ONTOLOGY)
		
		self.i.log.write("createDescribingProperty("+user_id+", "+property_name+")\n\n")
		
		# TODO: care about the value's datatype     vvvvv                 ^^^^^
		# if value_type_uri != None: # there is a given value type
		
		describing_property_uri = makeUriWithUserId(user_id, kill_spaces(property_name))
		
		ontology = self.getOntologyContaining(describing_property_uri)
		if ontology == None: # the property does not exist yet, we have to create it "on the go"
			# "create a new rdfs:Property"
			error_code = self.i.registerStatement(describing_property_uri, RDF_TYPE, RDF_PROPERTY)
			# "a sub-property of pimo:describingProperty"
			error_code = self.i.registerStatement(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
			# "has an rdfs:label"
			error_code = self.i.registerStatement(describing_property_uri, RDFS_LABEL, property_name, XMLSCHEMA_STRING)
		else:
			raise CanNotException("\""+ property_name +"\" already exists as a pimo:DescribingProperty for user \"" + user_id + "\". Can not create it.")
			
		#TODO: if (the error_codes say the creation is ok):
		return describing_property_uri
		# else:
		# 	return something else (None?)
			
############################################################

	# param value - describes the resource : String
	def deleteDescribingProperty(self, describing_property_uri):
		
		self.i.log.write("deleteDescribingProperty("+describing_property_uri+")\n\n")
		
		# TODO: care about the value's datatype
		
		# Check that the given property is a subclass of DescribingProperty.
		query = "SELECT * WHERE " + makeExistenceWhereClause(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
		graphs = self.i.executeQuery(query)
		
		if graphs.next(): # the given property really exists as a subPropertyOf DescribingProperty
			graphs.close()
			error_code = self.i.deleteStatement(describing_property_uri, RDF_TYPE, RDF_PROPERTY)
			error_code = self.i.deleteStatement(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
			error_code = self.i.deleteAllStatements(describing_property_uri, RDFS_LABEL, "?")
		else:
			graphs.close()
			raise CanNotException("\""+ describing_property_uri +"\" does not exist as a pimo:DescribingProperty. Can not delete it.")
		
		# TODO: delete all the valuedDescriptions using this property (or not)
		
		return 0 # TODO: returns a combination of error_codes...
		
############################################################

	# param value - describes the resource : String
	def isDescribingProperty(self, property_uri):
		
		self.i.log.write("isDescribingProperty("+property_uri+")\n\n")
		
		# Check that the given property is a subclass of DescribingProperty.
		query = "SELECT * WHERE " + makeExistenceWhereClause(property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
		graphs = self.i.executeQuery(query)
		
		if graphs.next(): # the given property exists as a subPropertyOf DescribingProperty
			graphs.close()
			return True
		else:
			graphs.close()
			return False
		
############################################################

	def addValuedDescription(self, thing_uri, describing_property_uri, value):
		
		# TODO: deal with that max_cardinality=1 stuff
		
		# as explained at http://www.dfki.uni-kl.de/~sauermann/2006/01-pimo-report/pimOntologyLanguageReport.html#ch-Representing%20Things (available on the 2008-06-17): 
		# "The user creates describing properties on the go. If a new property is needed, it is instantly created. There is no strong domain/range typing, if the user wants to use the property in an "unintended" way, the user is always right to do so."
		
		logger.debug("addValuedDescription, thing_uri = " + thing_uri)
		logger.debug("addValuedDescription, describing_property_uri = " + describing_property_uri)
		
		# Check that thing_uri really is a pimo:Thing
		if not self.i.instanceOf(thing_uri, PIMO_THING):
			raise TypeError("Can not add a describing property to \"" + thing_uri + "\" because it is not an instance of pimo:Thing.")
		
		# Check that describing_property_uri is a subPropertyOf DescribingProperty
		query = "SELECT * WHERE " + makeExistenceWhereClause(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
		graphs = self.i.executeQuery(query)
		
		if graphs.next():
			logger.debug(graphs.current()[0]["relation"][1])
			graphs.close()
		else:
			graphs.close()
			raise CanNotException("\""+ describing_property_uri +"\" is not a pimo:DescribingProperty. You have to create that property before you use it.")
		
		# ( example: "create the triple: paul:Rome paul:inhabitants "2.8 mio"" )
		# TODO: care about the value's datatype
		error_code = self.i.registerStatement(thing_uri, describing_property_uri, value, XMLSCHEMA_STRING)
		
		return error_code
		
############################################################

	def removeValuedDescription(self, thing_uri, describing_property_uri, value):
		
		# remove the binding between the resource and the value
		error_code = self.i.deleteStatement(thing_uri, describing_property_uri, value, XMLSCHEMA_STRING)
		
		return error_code
	
############################################################

	# @param super_property the parent property of the property given.
	def createProperty(self, user_id, property_name, super_property_uri, inverse_property_name):
		
		# Create the container Ontology (= storing graph = context) if not already existing.
		if self.i.getContextType(DEFAULT_ONTOLOGY_URI) == None:
			self.i.createContext(DEFAULT_ONTOLOGY_URI, NRL_ONTOLOGY)
		
		# TODO: test this error case.
		try:
			[PIMO_PARTOF, PIMO_HASPART, PIMO_HASTOPIC, PIMO_ISTOPICOF, PIMO_RELATED].index(super_property_uri)
		except:
			raise CanNotException("The super property given is not a valid super property. Can not create property.")
		
		property_uri = makeUriWithUserId(user_id, kill_spaces(property_name))
		inverse_property_uri = makeUriWithUserId(user_id, kill_spaces(inverse_property_name))
		
		# check that the property_uri is not taken yet
		ontology = self.getOntologyContaining(property_uri)
		if ontology != None: # that property already exists!
			raise CanNotException("The property \"" + property_name + "\" already exists for user \"" + user_id + "\". Can not create it.")
		
		# check that the inverse_property_uri is not taken yet
		ontology = self.getOntologyContaining(inverse_property_uri)
		if ontology != None: # that property already exists!
			raise CanNotException("The inverse property \"" + inverse_property_name + "\" already exists for user \"" + user_id + "\". Can not create it.")
		
		# Let us create that property
		# paul:supervises - rdf:type - rdf:Property
		error_code = self.i.registerStatement(property_uri, RDF_TYPE, RDF_PROPERTY)
		# paul:supervises - rdfs:subPropertyOf - pimo:related
		error_code = self.i.registerStatement(property_uri, RDFS_SUBPROPERTYOF, super_property_uri)
		# paul:supervises - rdfs:label - "supervises"
		error_code = self.i.registerStatement(property_uri, RDFS_LABEL, property_name, XMLSCHEMA_STRING)
		
		# paul:supervises - protege:inverseProperty - paul:isSupervisedBy
		error_code = self.i.registerStatement(property_uri, PROTEGE_INVERSEPROPERTY, inverse_property_uri)
		# paul:isSupervisedBy- protege:inverseProperty - paul:supervises
		error_code = self.i.registerStatement(inverse_property_uri, PROTEGE_INVERSEPROPERTY, property_uri)
		# paul:isSupervisedBy - rdf:type - rdf:Property
		error_code = self.i.registerStatement(inverse_property_uri, RDF_TYPE, RDF_PROPERTY)
		# paul:isSupervisedBy - rdfs:subPropertyOf - pimo:related
		error_code = self.i.registerStatement(inverse_property_uri, RDFS_SUBPROPERTYOF, INVERSE.get(super_property_uri))
		# paul:isSupervisedBy - rdfs:label - "is supervised by"
		error_code = self.i.registerStatement(inverse_property_uri, RDFS_LABEL, inverse_property_name, XMLSCHEMA_STRING)
		
		#TODO: if (the error_codes say the creation is ok):
		return property_uri
		# else:
		# 	return something else (None?)
	
############################################################

	def deleteProperty(self, property_uri):
		
		# TODO: check that this user "has the right" to remove this property, e.g. if he is the creator... Where to find this information?
		# 		vvv		 
		# special cases:
		# - do not remove that property if there are children properties of it (special function to remove anyway?)
		# - OR remove all the children properties
		# - OR create a link between grandpa and grandson if the father is removed (when possible!)
		
		# Check that the given property has no child property.
		query = "SELECT ?child WHERE { ?child <" + RDFS_SUBPROPERTYOF + "> <" + property_uri + "> }"
		children = self.i.executeQuery(query)
		
		if children.next():
			child1 = children.current[0]["child"][1]
			children.close()
			raise CanNotException("You can not delete the property \"" + property_uri + "\" because it has at least one child property: " + child1 + ".")
		
		children.close()
		
		# Check that this property exists
		#query = "SELECT * { <" + property_uri + "> <" + RDF_TYPE + "> <" + RDF_PROPERTY + "> }"
		#props = self.i.executeQuery(query)
		#if not props.next():
			#raise CanNotException("The property \"" + property_uri + "\" does not exist.")
		#props.close()
		
		# Let us remove that property
		error_code = self.i.deleteStatement(property_uri, RDF_TYPE, RDF_PROPERTY)
		error_code = self.i.deleteAllStatements(property_uri, RDFS_SUBPROPERTYOF, "?")
		error_code = self.i.deleteAllStatements(property_uri, RDFS_LABEL, "?")
		
		inverse_property_uri = self.i.getPimosInversePropertyOf(property_uri)
		logger.debug("inverse_property_uri = "+inverse_property_uri)
		
		if inverse_property_uri != None:
			error_code = self.i.deleteStatement(inverse_property_uri, RDF_TYPE, RDF_PROPERTY)
			error_code = self.i.deleteAllStatements(inverse_property_uri, RDFS_SUBPROPERTYOF, "?")
			error_code = self.i.deleteAllStatements(inverse_property_uri, RDFS_LABEL, "?")
			error_code = self.i.deleteStatement(property_uri, PROTEGE_INVERSEPROPERTY, inverse_property_uri)
			error_code = self.i.deleteStatement(inverse_property_uri, PROTEGE_INVERSEPROPERTY, property_uri)
		
		return 0 # TODO: returns a combination of error_codes...

############################################################

	# @param resource_uri the URI of the resource(of type Thing: web page, e-mail, file, etc.).
	# @param property_uri the URI of the property.
	# @param object_uri the URI of the object (of type Thing: other web page, e-mail, file, etc.).
	def addRelation(self, thing_uri, property_uri, object_uri):
		
		# check that the resource is (or derives from) a pimo:Thing
		if not self.i.instanceOf(thing_uri, PIMO_THING):
			raise TypeError("Can not add a relation concerning \"" + thing_uri + "\" because it is not an instance of pimo:Thing.")
		
		# check that the object is (or derives from) a pimo:Thing
		if not self.i.instanceOf(object_uri, PIMO_THING):
			raise TypeError("Can not add a relation concerning \"" + object_uri + "\" because it is not an instance of pimo:Thing.")
		
		ontology = self.getOntologyContaining(property_uri)
		if ontology == None: # that property does not exists
			raise CanNotException("The property \"" + property_uri + "\" does not exists. You have to create a relation before you use it.")
		
		# Let us register the relation between these two Things
		error_code = self.i.registerStatement(thing_uri, property_uri, object_uri)
		
		return error_code
	
############################################################

	# @param resource_uri the URI of the resource(of type Thing: web page, e-mail, file, etc.).
	# @param property_uri the URI of the property.
	# @param object_uri the URI of the object (of type Thing: other web page, e-mail, file, etc.).
	def removeRelation(self, resource_uri, property_uri, object_uri):
		
		# TODO: do we type-check here?
		# Let us remove the relation between these two Things
		error_code = self.i.deleteStatement(resource_uri, property_uri, object_uri)
		
		return error_code

############################################################

	# registerAndReify() creates the pimo:ResourceManifestation representing the given resource_uri and then reifies it, i.e. creates a pimo:Thing corresponding to it.
	
	# Further programming of this class should take into account the possibilities of automatic properties addings described at http://www.dfki.uni-kl.de/~sauermann/2006/01-pimo-report/pimOntologyLanguageReport.html#ch-AutomaticCreationofThingsfromResourceManifestations (available on the 2008-06-19).
	
	# @param resource_type is given by the programmer of the calling method, depending on wether the resource is a web page, an e-mail, a file, etc.
	# @param resource_thing_name is the name that is going to be given to that element (regardless to its type).
	
	# Returns the URI of the Thing that represents the given resource.
	def registerAndReify(self, user_id, resource_uri, resource_type, resource_thing_name, resource_thing_type=PIMO_THING):
		
		# a) Check that, if the resource_uri is already registered, its type is the same as the one given here.
		my_type = self.i.getImage(resource_uri, RDF_TYPE)
		if my_type != None and my_type != resource_type:
			raise TypeError("\"" + resource_uri + "\" already has type \"" + my_type + "\" in database, so you can not use it with type \"" + resource_type + "\".")
		
		resource_thing_uri = makeUriWithUserId(user_id, kill_spaces(resource_thing_name))
		# b) Check that, if the resource_thing_uri is already registered, its type is the same as the one given here.
		my_thing_type = self.i.getImage(resource_thing_uri, RDF_TYPE)
		if my_thing_type != None and my_thing_type != resource_thing_type:
			raise TypeError("\"" + resource_thing_name + "\" already has type \"" + my_thing_type + "\" in database, so you can not use it with type \"" + resource_thing_type + "\".")
		
		# c) Check that the resource_thing_type derives from pimo:Thing.
		if not self.i.derivesFrom(resource_thing_type, PIMO_THING):
			raise TypeError("\"" + resource_uri + "\" can not be reified to type \"" + resource_thing_type + "\" because it does not derive from pimo:Thing.")
		
		# d) Check that, if the resource_thing_uri is already registered, the given resource_uri corresponds to the one registered as the grounding occurrence for this Thing.
		groundingOccurrence = self.i.getImage(resource_thing_uri, PIMO_GROUNDINGOCCURRENCE)
		if (groundingOccurrence != None) and (groundingOccurrence != resource_uri):
			raise CanNotException("The resource named \"" + resource_thing_name + "\" already exists and is linked to \"" + str(groundingOccurrence) + "\", so it can not be linked to \"" + resource_uri + "\".")
		
		
		# 1. Register the resource as a pimo:ResourceManifestation of the given type.
			# secured by checking a)
		error_code = self.i.registerStatement(resource_uri, RDF_TYPE, resource_type)
		
		# 2. Register the resource as a pimo:Thing (or derived type).
			# secured by checking b) and c)
		error_code = self.i.registerStatement(resource_thing_uri, RDF_TYPE, resource_thing_type)
		
		# 3. Link the two elements above with property groundingOccurrence.
			# secured by checking d)
		# If the link already exists, does not affect the database badly because the statement is the same (no duplicates).
		# TODO: tocheck, if this is still true when dealing with multiple contexts (storing graphs). If it is not, we have to check its non-existence before adding it.
		error_code = self.i.registerStatement(resource_thing_uri, PIMO_GROUNDINGOCCURRENCE, resource_uri)
		
		# 4. SET the label for the freshly created pimo:Thing
			# secured by the fact that we SET and NOT ADD the label statement (will erase the (possibly existing) previous label)
		error_code = self.i.setImage(resource_thing_uri, RDFS_LABEL, resource_thing_name, XMLSCHEMA_STRING)
		
		return resource_thing_uri
	
	
	